#include "states.h"

#include "../command.h"
#include "../console.h"

using namespace cli_parse;

ModuleStates::ModuleStates (void) : cl_picked_sector (0, true,  -1),
                                    cl_picked_line   (0, false, -1) {
// State initialization removed in favor of a config file...
//   (they are still listed here for authoritative reference).
/*
  r_tex                 = true;
  r_tex_align           = false;
  r_animate             = true;
  r_wireframe           = false;
  r_triangle_strips     = false;
  r_tesselate_triangles = false;
  r_global_brightness   = 1.0f;
  r_combine_batches     = true;
  r_shadows             = false;
  r_depth_first         = false;
  r_null                = false;
  r_light_scale         = 1.75f;

  r_fog                 = true;
  r_fog_start           = 10.0f;
  r_fog_end             = 2500.0f;
  r_fog_density         = 0.000444f;
  r_fog_mode            = 0;

  cl_all_rooms          = false;
  cl_cur_room           = 1;
  cl_cam_room           = 1;
  cl_need_pick          = 0;
  cl_level_num          = 0;

  cl_collision          = true;
  cl_gravity            = true;
*/

  *cli_file_name        = _T ('\0');

  ZeroMemory (ulTextures,       sizeof (e3dTexture*) * (MAX_TEXTURES + 1));
  ZeroMemory (ulTexNormals,     sizeof (e3dTexture*) * (MAX_TEXTURES + 1));
  ZeroMemory (ulFlatTextures,   sizeof (e3dTexture*) * (MAX_FLATS    + 1));
  ZeroMemory (ulFlatNormals,    sizeof (e3dTexture*) * (MAX_TEXTURES + 1));
  ZeroMemory (ulEffectTextures, sizeof (e3dTexture*) * (MAX_BITMAPS  + 1));

  ZeroMemory (loadedTextures,       sizeof (bool) * (MAX_TEXTURES + 1));
  ZeroMemory (loadedFlatTextures,   sizeof (bool) * (MAX_FLATS    + 1));
  ZeroMemory (loadedEffectTextures, sizeof (bool) * (MAX_BITMAPS  + 1));


  eTB_CommandProcessor* command = ::states->command;

  command->AddVariable ("r_wireframe",   new eTB_VarStub <bool>  (&r_wireframe,  this));
  command->AddVariable ("r_tex",         new eTB_VarStub <bool>  (&r_tex));
  command->AddVariable ("r_tex_align",   new eTB_VarStub <bool>  (&r_tex_align,  this));
  command->AddVariable ("r_animate",     new eTB_VarStub <bool>  (&r_animate));
  command->AddVariable ("r_actors",      new eTB_VarStub <bool>  (&r_actors));
  command->AddVariable ("r_shadows",     new eTB_VarStub <bool>  (&r_shadows,    this));
  command->AddVariable ("r_depth_first", new eTB_VarStub <bool>  (&r_depth_first));
  command->AddVariable ("r_null",        new eTB_VarStub <bool>  (&r_null));
  command->AddVariable ("r_light_scale", new eTB_VarStub <float> (&r_light_scale, this));

  command->AddVariable ("r_fog",         new eTB_VarStub <bool>  (&r_fog));
  command->AddVariable ("r_fog_start",   new eTB_VarStub <float> (&r_fog_start));
  command->AddVariable ("r_fog_end",     new eTB_VarStub <float> (&r_fog_end));
  command->AddVariable ("r_fog_density", new eTB_VarStub <float> (&r_fog_density));
  command->AddVariable ("r_fog_mode",    new eTB_VarStub <int>   (&r_fog_mode));

  command->AddVariable ("cl_all_rooms",  new eTB_VarStub <bool> (&cl_all_rooms));
  command->AddVariable ("cl_collision",  new eTB_VarStub <bool> (&cl_collision));
  command->AddVariable ("cl_gravity",    new eTB_VarStub <bool> (&cl_gravity));

  command->AddVariable ("r_deferred_mode",   new eTB_VarStub <int>   (&r_deferred_mode));
  command->AddVariable ("r_deferred_scale",  new eTB_VarStub <float> (&r_deferred_scale));
  command->AddVariable ("r_lights",          new eTB_VarStub <bool>  (&r_lights));
  command->AddVariable ("r_hdr",             new eTB_VarStub <bool>  (&r_hdr));
  command->AddVariable ("r_ssao",            new eTB_VarStub <bool>  (&r_ssao));

  /* These two states are for internal use only, see cl_room_num! */
  //command->AddVariable ("cl_cur_room",    new eTB_VarStub <int> ((int *)&cl_cur_room));
  //command->AddVariable ("cl_cam_room",    new eTB_VarStub <int> ((int *)&cl_cam_room));

  command->AddVariable ("cl_need_pick",   new eTB_VarStub <bool> (&cl_need_pick));
  command->AddVariable ("cl_pause",       new eTB_VarStub <bool> (&cl_pause));

  command->AddVariable ("cl_level_num",   new eTB_VarStub <int> ((int *)&cl_level_num, this));
  command->AddVariable ("cl_room_num",    new eTB_VarStub <int> ((int *)&cl_room_num,  this));

  command->AddVariable ("cl_picked_line",   new eTB_VarStub <short> ((short *)&cl_picked_line.surface,   this));
  command->AddVariable ("cl_picked_sector", new eTB_VarStub <short> ((short *)&cl_picked_sector.surface, this));

  command->AddVariable ("r_debug_tex_id",    new eTB_VarStub <int> (&r_debug_tex_id));
  command->AddVariable ("r_debug_flat_id",   new eTB_VarStub <int> (&r_debug_flat_id));
  command->AddVariable ("r_debug_effect_id", new eTB_VarStub <int> (&r_debug_effect_id));

  command->AddVariable ("r_texgen",          new eTB_VarStub <int> (&r_texgen, this));
  command->AddVariable ("r_debug_tex_line",  new eTB_VarStub <int> (&r_debug_tex_line));



  /// Deprecated!
  command->AddVariable ("r_combine_batches",   new eTB_VarStub <bool>  (&r_combine_batches));
  command->AddVariable ("r_global_brightness", new eTB_VarStub <float> (&r_global_brightness));

  command->AddVariable ("r_triangle_strips",     new eTB_VarStub <bool> (&r_triangle_strips));
  command->AddVariable ("r_tesselate_triangles", new eTB_VarStub <bool> (&r_tesselate_triangles));
}


template <typename T>
void
SetVarVal (T, eTB_Variable* stub, void* val)
{
  *(((eTB_VarStub <T> *)stub)->getValuePtr ()) = *(T*)val;
}


#include "level.h"
#include "room.h"
#include "../window.h" // TODO: Move this to eTestbed's internal states

/* SetVarVal uses template typeid information, the value of x
     in the function below never matters... this is an unusual
       design, consider changing. */
bool
ModuleStates::OnVarChange (eTB_Variable* var, void* val)
{
  switch (var->getType ())
  {
    case eTB_Variable::Boolean:
    {
      bool x = false; /* We are just using this for template typeid... */

      /* TEMP HACK: Reload the shadow shaders... */
      if (((eTB_VarStub <bool>*) var)->getValuePtr () == (bool *)&r_shadows) {
        /* Reload the Shadow Shaders if r_shadows is true... */
        if (*(bool *)val == true) {
          extern void ReloadShadowShaders (void);
          ReloadShadowShaders ();
        }
      }

      if (((eTB_VarStub <bool>*) var)->getValuePtr () == (bool *)&r_tex_align) {
        extern bool LoadTexture       (const unsigned int, const bool&);
        extern bool LoadFlatTexture   (const unsigned int, const bool&);
        extern bool LoadEffectTexture (unsigned int);

        if (*(bool *)val == true) {
          /* Load the debug texture before altering this state... */
          LoadTexture       (MAX_TEXTURES, false);
          LoadFlatTexture   (MAX_FLATS,    false);
          LoadEffectTexture (MAX_BITMAPS);
        }
      }

      SetVarVal (x, var, val);
    } break;

    case eTB_Variable::Float:
    {
      float x = 0;

      // When the light scale changes, we need to reload the current room...
      if (((eTB_VarStub <float>*) var)->getValuePtr () == &r_light_scale) {
        SetVarVal (x, var, val);

        // If set in the configuration file before a level is loaded, this would
        //  otherwise crash :P
        if (Underlight::lvl != NULL) {
          Underlight::lvl->rooms [states->cl_player.GetRoom ()]->Unload ();
          Underlight::lvl->rooms [states->cl_player.GetRoom ()]->Load   (Underlight::lvl);
        }
      }
   } break;

    case eTB_Variable::Int:
    {
      int x = 0; /* We are just using this for template typeid... */
//      SetVarVal (x, var, val);

      if (((eTB_VarStub <int>*) var)->getValuePtr () == (int *)&cl_level_num) {
        if (*(int *)val >= 46/*level->NumLevels ()*/ ||
            *(int *)val < 1)
          return false;

        extern bool load_level (int lvl_num);

        bool ret = load_level (*(int *)val);

        if (! ret)
          return false;

        if (ret) {
          SetVarVal (x, var, val);

          //
          // Search for a formal entry point
          //
          //  * Teleport to it, if one is found.
          //
          std::vector<cli_parse::EntryPoint*>::const_iterator entry =
              Underlight::lvl->entry_points.begin ();
          std::vector<cli_parse::EntryPoint*>::const_iterator end   =
              Underlight::lvl->entry_points.end ();

          while (eSTL::IterValid (entry, end)) {
            if ((*entry)->GetType () == 0) {
              float x     = (*entry)->GetX     ();
              float y     = (*entry)->GetY     ();
              float angle = (*entry)->GetAngle ();
              int   room  = (*entry)->GetRoom  ();

              cli_parse::states->cl_player.Teleport (room, x, y, angle);
            }

            entry++;
          }
        }

        SetVarVal (x, var, val);
      }

      else if (((eTB_VarStub <int>*) var)->getValuePtr () == (int *)&cl_room_num) {
        int room_num = *(int *)val;
        if (room_num < (int)(Underlight::lvl->rooms.size () - 1) &&
            room_num >= 0) {
          e3dVector2 vPos = Underlight::lvl->rooms [room_num]->bound.Center ();

          cli_parse::states->cl_player.Teleport (room_num, vPos.x, vPos.y);

          SetVarVal (x, var, val);
        }
      }

      // When texgen changes, we need to reload the current room...
      else if (((eTB_VarStub <int>*) var)->getValuePtr () == (int *)&r_texgen) {
        SetVarVal (x, var, val);

        // If set in the configuration file before a level is loaded, this would
        //  otherwise crash :P
        if (Underlight::lvl != NULL) {
          Underlight::lvl->rooms [states->cl_player.GetRoom ()]->Unload ();
          Underlight::lvl->rooms [states->cl_player.GetRoom ()]->Load   (Underlight::lvl);
        }
      }
    } break;

    case eTB_Variable::Short:
    {
      short x = 0;  /* We are just using this for template typeid... */

      if (((eTB_VarStub <short>*) var)->getValuePtr () == (short *)&cl_picked_sector.surface) {
        cl_picked_sector.surface = *((short *)val);
        cl_picked_sector.room    = cl_cam_room;
        SetVarVal (x, var, val);
      }

      else if (((eTB_VarStub <short>*) var)->getValuePtr () == (short *)&cl_picked_line.surface) {
        cl_picked_line.surface = *((short *)val) + MAX_SECTORS;
        cl_picked_line.room    = cl_cam_room;
        SetVarVal (x, var, val);
      }
    } break;
  }

  return true;
}
